using Devil;
using System.Collections.Generic;

namespace GameToolkit.Formula
{
    #region formula exception

    public class InvalidFormulaException : System.Exception
    {
        //public InvalidFormulaException() : base("不合法的公式格式") { }

        public InvalidFormulaException(string formula)
            : base(string.Format("不合法的公式:\n{0}", formula)) { }

    }

    public class InvalidFormulaOutputException : System.Exception
    {
        public InvalidFormulaOutputException() : base("不匹配的公式输出类型") { }
        public InvalidFormulaOutputException(IFormulaOutput output, string keyword)
            : base(string.Format("输出类型({0})不能匹配运算符({1})", output == null ? "null" : output.GetType().Name, keyword)) { }
    }

    #endregion

    // 比较类型
    public enum EComparision
    {
        Equal = 0,
        NotEqual = 1,
        Greater = 2,
        Less = 3,
        GEqual = 4,
        LEqual = 5,
    }

    /// <summary>
    /// 运算公式（表达式）
    /// </summary>
    public interface IFormula
    {
        /// <summary>
        /// 获取表达式结果
        /// </summary>
        /// <param name="context">当前上下文</param>
        /// <returns></returns>
        object GetFormulaResult(object context);
    }

    /// <summary>
    /// 运算符源代码
    /// </summary>
    public interface IFormulaSource
    {
        int Id { get; }
        string Keyword { get; }
    }

    /// <summary>
    /// 公式编译/解析状态
    /// </summary>
    public interface ICompilingFormulaState
    {
        /// <summary>
        /// 获取真正的关键词
        /// </summary>
        /// <param name="keywordOrAlias"></param>
        /// <returns></returns>
        string GetRealKeyword(string keywordOrAlias);
        /// <summary>
        /// 解析变量
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="v"></param>
        /// <returns></returns>
        bool GetPredefinedValue(string keyword, out object v);

        /// <summary>
        /// 查找不区分大小写的变量名定义
        /// </summary>
        /// <param name="ignoreCaseName"></param>
        /// <returns></returns>
        string GetPredefinedName(string ignoreCaseName);
    }

    #region formula output defination

    /// <summary>
    /// 公式运算输出
    /// </summary>
    public interface IFormulaOutput { }

    /// <summary>
    /// 引用类型公式输出
    /// </summary>
    public interface IReferenceFormulaOutput : IFormulaOutput
    {
        object GetFormulaResult(object context, string varName);
        //object GetFormulaResult(object context, object input, int id, object arg);
    }

    public interface IReferenceFormulaCompilier : IFormulaOutput
    {
        bool CompileOutput(ICompilingFormulaState rt, IFormulaSource input);
    }

    public interface IPrecompileReferenceFormula : IFormulaOutput
    {
        bool IsTextCompilier { get; }
        bool OnPrecompile(ICompilingFormulaState rt, string keyword, out object result);
    }

    /// <summary>
    /// 方法型公式输出
    /// </summary>
    public interface IFunctionFormulaOutput : IFormulaOutput
    {
        object GetFormulaResult(object context, string funcName, object arg);
    }

    /// <summary>
    /// 方法编译器
    /// </summary>
    public interface IFunctionFormulaCompilier : IFormulaOutput
    {
        bool CompileOutput(ICompilingFormulaState rt, IFormulaSource input, IFormulaSource args);
    }

    public interface IPrecompileFunctionFormula : IFormulaOutput
    {
        /// <summary>
        /// 是否允许公式作为预编译输入
        /// </summary>
        bool IsFormulaInputEnabled { get; }
        bool OnPrecompile(ICompilingFormulaState rt, string keyword, object arg, out object result);
    }

    /// <summary>
    /// 单目运算公式
    /// </summary>
    public interface IUnaryFormulaOutput : IFormulaOutput
    {
        object GetFormulaResult(object context, object input);
    }

    /// <summary>
    /// 单目运算符编译器
    /// </summary>
    public interface IUnaryFormulaCompilier : IFormulaOutput
    {
        bool CompileOutput(ICompilingFormulaState rt, IFormulaSource input, IFormulaSource child);
    }

    /// <summary>
    /// 预编译单目运算结果
    /// </summary>
    public interface IPrecompileUnaryFormula : IFormulaOutput
    {
        /// <summary>
        /// 是否允许公式作为预编译输入
        /// </summary>
        bool IsFormulaInputEnabled { get; }
        bool OnPrecompile(ICompilingFormulaState rt, object input, out object result);
    }

    /// <summary>
    /// 双目运算符公式
    /// </summary>
    public interface IBinaryFormulaOutput : IFormulaOutput
    {
        object GetFormulaResult(object context, object inputA, object inputB);
    }

    /// <summary>
    /// 双目运算符编译器
    /// </summary>
    public interface IBinaryFormulaCompilier : IFormulaOutput
    {
        bool CompileOutput(ICompilingFormulaState rt, IFormulaSource input, IFormulaSource left, IFormulaSource right);
    }

    /// <summary>
    /// 预编译双目运算结果
    /// </summary>
    public interface IPrecompileBinaryFormula : IFormulaOutput
    {
        /// <summary>
        /// 是否允许公式作为预编译输入
        /// </summary>
        bool IsFormulaInputEnabled { get; }
        bool OnPrecompile(ICompilingFormulaState rt, object inputA, object inputB, out object result);
    }

    #endregion

    public enum EFormulaOperatePriority
    {
        LogicBinary = 10000,
        MathBinary = 20000,
        Unary = 30000,
        Function = 40000,
        Highest = 1000000,
    }

    public class FormulaResult : IFormula
    {
        object value;
        public FormulaResult(object value)
        {
            this.value = value;
        }

        public object GetFormulaResult(object context)
        {
            return value;
        }
        public override string ToString()
        {
            return value == null ? "null" : value.ToString();
        }
    }

    public class FormulaFactory
    {
        public static bool CompileBuiltinValue(string keyword, out object v)
        {
            int n;
            float f;
            if (Localization.TryParse(keyword, out n))
            {
                v = n;
                return true;
            }
            else if (Localization.TryParse(keyword, out f))
            {
                v = f;
                return true;
            }
            else if (keyword.EqualsIgnoreCase("true") || keyword.EqualsIgnoreCase("yes"))
            {
                v = true;
                return true;
            }
            else if (keyword.EqualsIgnoreCase("false") || keyword.EqualsIgnoreCase("no"))
            {
                v = false;
                return true;
            }
            else
            {
                v = null;
                return false;
            }
        }

        internal class CompilingStack : ICompilingFormulaState
        {
            internal CompilingState mState;
            HashSet<string> mExcludeAlias;
            internal CompilingStack()
            {
                mExcludeAlias = new HashSet<string>();
            }

            public bool GetPredefinedValue(string keyword, out object v)
            {
                return mState.GetPredefinedValue(keyword, out v);
            }

            public string GetRealKeyword(string keywordOrAlias)
            {
                if (mState != null && mExcludeAlias.Add(keywordOrAlias))
                    return mState.GetRealKeyword(keywordOrAlias);
                else
                    return keywordOrAlias;
            }
            public string GetPredefinedName(string ignoreCaseName)
            {
                return mState.GetPredinfedName(ignoreCaseName);
            }

            internal void Clear()
            {
                mState = null;
                mExcludeAlias.Clear();
            }
        }

        internal class CompilingState
        {
            internal FormulaFactory factory;
            internal readonly Dictionary<string, string> alias;
            internal readonly Dictionary<string, object> values;

            public CompilingState()
            {
                alias = new Dictionary<string, string>();
                values = new Dictionary<string, object>();
            }

            internal void Clear()
            {
                alias.Clear();
                values.Clear();
            }

            public string GetRealKeyword(string keywordOrAlias)
            {
                string str;

                if (alias.TryGetValue(keywordOrAlias, out str))
                    return str;
                else if (factory != null && factory.mAlias.TryGetValue(keywordOrAlias, out str))
                    return str;
                else
                    return keywordOrAlias;
            }

            public bool GetPredefinedValue(string keyword, out object v)
            {
                if (values.TryGetValue(keyword, out v))
                {
                    return true;
                }
                else if (factory != null && factory.mValues.TryGetValue(keyword, out v))
                {
                    return true;
                }
                else
                {
                    v = null;
                    return false;
                }
            }

            public string GetPredinfedName(string ignoreCaseName)
            {
                foreach (var k in values.Keys)
                {
                    if (k.EqualsIgnoreCase(ignoreCaseName))
                        return k;
                }
                return factory.GetPredefinedName(ignoreCaseName);
            }
        }



        readonly object _lock = new object();
        Dictionary<string, string> mAlias;
        Dictionary<string, object> mValues;
        Dictionary<int, FormulaOperator> mFormulaOperators;
        AtomFormula mAtomFormula;
        readonly int mAliasCmdId;
        readonly int mValueCmdId;
        bool mInitialized;

        ObjectPool<CompilingState> mRtPools;
        ObjectPool<CompilingStack> mStackPools;

        // copy config from other factory
        public FormulaFactory(FormulaFactory baseFac)
        {
            if (baseFac == null)
                throw new System.ArgumentNullException("baseFac");

            mAlias = new Dictionary<string, string>();
            mValues = new Dictionary<string, object>();

            mAtomFormula = baseFac.mAtomFormula;
            mRtPools = baseFac.mRtPools;
            mStackPools = baseFac.mStackPools;
            mAliasCmdId = baseFac.mAliasCmdId;
            mValueCmdId = baseFac.mValueCmdId;

            mFormulaOperators = new Dictionary<int, FormulaOperator>();

            lock (baseFac._lock)
            {
                foreach (var kv in baseFac.mFormulaOperators)
                {
                    mFormulaOperators.Add(kv.Key, kv.Value);
                }

                foreach (var kv in baseFac.mValues)
                {
                    mValues.Add(kv.Key, kv.Value);
                }

                foreach (var kv in baseFac.mAlias)
                {
                    mAlias.Add(kv.Key, kv.Value);
                }
            }
        }

        public FormulaFactory(bool initDefaultLogic = true, bool initDefaultMath = true, int compileBufferSize = 8)
        {
            //lock (_lock)
            //{
            mAtomFormula = new AtomFormula(0, (int)EFormulaOperatePriority.Highest, EAtomType.Auto);
            mAlias = new Dictionary<string, string>();
            mValues = new Dictionary<string, object>();

            mRtPools = new ObjectPool<CompilingState>(compileBufferSize > 4 ? compileBufferSize : 4, () => new CompilingState(), (p) => p.Clear());
            mStackPools = new ObjectPool<CompilingStack>(mRtPools.Capacity << 2, () => new CompilingStack(), (p) => p.Clear());
            mFormulaOperators = new Dictionary<int, FormulaOperator>();

            FormulaOperator oper;

            oper = new AtomFormula((int)',', 0, EAtomType.Subfix);
            oper.keyword = ",";
            mFormulaOperators[oper.id] = oper;
            mFormulaOperators[ParallelUtils.IgnoreCaseToHash("，")] = oper;

            oper = new AtomFormula(ParallelUtils.IgnoreCaseToHash(")"), 0, EAtomType.Subfix);
            oper.keyword = ")";
            mFormulaOperators[oper.id] = oper;
            mFormulaOperators[ParallelUtils.IgnoreCaseToHash("）")] = oper;

            oper = new BracketFormula(ParallelUtils.IgnoreCaseToHash("("), oper.id, int.MaxValue);
            oper.keyword = "(";
            mFormulaOperators[oper.id] = oper;
            mFormulaOperators[ParallelUtils.IgnoreCaseToHash("（")] = oper;

            if (initDefaultLogic)
            {
                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("&"), (int)EFormulaOperatePriority.LogicBinary);
                oper.keyword = "&";
                mFormulaOperators[oper.id] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("and")] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("&&")] = oper;

                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("|"), (int)EFormulaOperatePriority.LogicBinary - 1);
                oper.keyword = "|";
                mFormulaOperators[oper.id] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("or")] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("||")] = oper;

                oper = new RightJoinFormula(ParallelUtils.IgnoreCaseToHash("!"), (int)EFormulaOperatePriority.Unary - 1);
                oper.keyword = "!";
                mFormulaOperators[oper.id] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("！")] = oper;
                mFormulaOperators[ParallelUtils.IgnoreCaseToHash("not")] = oper;

            }

            if (initDefaultMath)
            {
                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("+"), (int)EFormulaOperatePriority.MathBinary - 1);
                oper.keyword = "+";
                mFormulaOperators[oper.id] = oper;

                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("-"), (int)EFormulaOperatePriority.MathBinary - 1);
                oper.keyword = "-";
                mFormulaOperators[oper.id] = oper;

                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("*"), (int)EFormulaOperatePriority.MathBinary);
                oper.keyword = "*";
                mFormulaOperators[oper.id] = oper;

                oper = new BinFormula(ParallelUtils.IgnoreCaseToHash("/"), (int)EFormulaOperatePriority.MathBinary);
                oper.keyword = "/";
                mFormulaOperators[oper.id] = oper;
            }

            mValues["null"] = null;

            mAliasCmdId = ParallelUtils.IgnoreCaseToHash("#alias#");
            mValueCmdId = ParallelUtils.IgnoreCaseToHash("#value#");
            //}
        }

        /// <summary>
        /// 调用该方法关闭初始化通道，并且准备执行编译命令
        /// </summary>
        public void GetReadyForCompilingFormula()
        {
            lock (_lock)
            {
                mInitialized = true;
            }
        }

        /// <summary>
        /// 设置默认运算方法
        /// </summary>
        /// <param name="formula"></param>
        /// <exception cref="System.Exception"></exception>
        public void SetFormulaOutput(IFormulaOutput formula)
        {
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");
                mAtomFormula.SetFormulaOutput(formula);
            }
        }

        /// <summary>
        /// 设置公式运算方法
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="formula"></param>
        /// <exception cref="System.Exception"></exception>
        public void SetFormulaOutput(string keyword, IFormulaOutput formula)
        {
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");
                var id = keyword.IgnoreCaseToHash();
                FormulaOperator oper;
                if (mFormulaOperators.TryGetValue(id, out oper))
                {
                    oper.SetFormulaOutput(formula);
                }
            }
        }

        /// <summary>
        /// 创建别名
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="alias"></param>
        /// <exception cref="System.Exception"></exception>
        public void AliasFormula(string keyword, string alias)
        {
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");

                var id = keyword.IgnoreCaseToHash();
                FormulaOperator oper;
                if (mFormulaOperators.TryGetValue(id, out oper))
                {
                    var id2 = alias.IgnoreCaseToHash();
                    if (id2 != id)
                        mFormulaOperators[id2] = oper;
                }
            }
        }

        /// <summary>
        /// 创建公式运算方法
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="priority"></param>
        /// <param name="formula"></param>
        /// <exception cref="System.Exception"></exception>
        public void CreateFormula(string keyword, int priority, IFormulaOutput formula)
        {
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");
                var id = keyword.IgnoreCaseToHash();
                FormulaOperator oper;
                bool create = !mFormulaOperators.TryGetValue(id, out oper);
                if (create)
                {
                    if (formula is IBinaryFormulaOutput || formula is IPrecompileBinaryFormula || formula is IBinaryFormulaCompilier)
                    {
                        oper = new BinFormula(id, priority);
                    }
                    else if (formula is IUnaryFormulaOutput || formula is IPrecompileUnaryFormula || formula is IUnaryFormulaCompilier)
                    {
                        oper = new RightJoinFormula(id, priority);
                    }
                    else if (formula is IFunctionFormulaOutput || formula is IPrecompileFunctionFormula || formula is IFunctionFormulaCompilier)
                    {
                        oper = new AtomFormula(id, priority, EAtomType.Function);
                    }
                    else if (formula is IReferenceFormulaOutput || formula is IPrecompileReferenceFormula || formula is IReferenceFormulaCompilier)
                    {
                        oper = new AtomFormula(id, priority, EAtomType.Variable);
                    }
                }
                if (oper != null)
                {
                    oper.SetFormulaOutput(formula);
                    if (create)
                    {
                        oper.keyword = keyword;
                        mFormulaOperators[id] = oper;
                    }
                }
            }
        }

        /// <summary>
        /// 设置预设值
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <exception cref="System.Exception"></exception>
        public void SetPredefinedValue(string name, object value)
        {
            if (string.IsNullOrEmpty(name))
                throw new System.Exception("invalid name for predefined value.");
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");
                mValues[name] = value;
            }
        }

        /// <summary>
        /// 获取预设值
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        /// <exception cref="System.Exception"></exception>
        public bool GetPredefinedValue(string name, out object value)
        {
            if (string.IsNullOrEmpty(name))
            {
                value = null;
                return false;
            }
            if (!mInitialized)
                throw new System.Exception("Formula factory is not ready for getting values.");
            return mValues.TryGetValue(name, out value);
        }

        /// <summary>
        /// 获取预设值命名
        /// </summary>
        /// <param name="ignoreCaseName"></param>
        /// <returns></returns>
        public string GetPredefinedName(string ignoreCaseName)
        {
            if (!mInitialized)
                throw new System.Exception("Formula factory is not ready for getting values.");
            foreach (var k in mValues.Keys)
            {
                if (ignoreCaseName.EqualsIgnoreCase(k))
                    return k;
            }
            return null;
        }

        public void VisitPredefinedValues(System.Action<string, object> action)
        {
            if (!mInitialized)
                throw new System.Exception("Formula factory is not ready for getting values.");
            foreach (var kv in mValues)
            {
                action(kv.Key, kv.Value);
            }
        }

        /// <summary>
        /// 获取值定义的所有别名
        /// </summary>
        /// <param name="value"></param>
        /// <param name="alias"></param>
        /// <exception cref="System.Exception"></exception>
        public void GetAliasByValue(object value, ICollection<string> alias)
        {
            if (value == null)
                return;

            if (!mInitialized)
                throw new System.Exception("Formula factory is not ready for getting alias.");

            foreach (var kv in mValues)
            {
                if (value.Equals(kv.Value))
                {
                    var name = kv.Key;
                    alias.Add(name);
                    foreach (var akv in mAlias)
                    {
                        if (akv.Value == name)
                        {
                            alias.Add(akv.Key);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 执行别名，初始值赋值命令 
        /// 赋值指令：#value# a=b
        /// 别名指令：#alias# a=b
        /// </summary>
        /// <param name="content"></param>
        /// <exception cref="System.Exception"></exception>
        /// <exception cref="InvalidFormulaException"></exception>
        public void ParseCmd(string content)
        {
            if (string.IsNullOrEmpty(content))
                return;
            lock (_lock)
            {
                if (mInitialized)
                    throw new System.Exception("Formula factory was closed initializing.");

                var reader = new SitcomFile();
                reader.Load(content);
                reader.BeginRead();
                while (reader.StartLine())
                {
                    if (!reader.NextKeyword())
                        continue;
                    var keyword = reader.keyword;
                    var isAlias = keyword.id == mAliasCmdId;
                    if (isAlias || keyword.id == mValueCmdId)
                    {
                        while (!reader.Eof)
                        {
                            string var = null;
                            SitcomFile.Keyword result = default;
                            if (reader.NextKeyword())
                            {
                                keyword = reader.keyword;
                                if (keyword.type == KeywordType.Content || keyword.type == KeywordType.Keyword)
                                    var = keyword.text;
                                else
                                    throw new InvalidFormulaException(reader.ToString());
                            }
                            else
                            {
                                break;
                            }
                            if (!reader.NextKeyword() || reader.keyword.id != (int)'=')
                            {
                                throw new InvalidFormulaException(reader.ToString());
                            }
                            if (reader.NextKeyword())
                            {
                                keyword = reader.keyword;
                                if (keyword.type == KeywordType.Keyword || keyword.type == KeywordType.Content)
                                    result = keyword;
                                else
                                    throw new InvalidFormulaException(reader.ToString());
                            }
                            else
                            {
                                throw new InvalidFormulaException(reader.ToString());
                            }
                            if (isAlias)
                            {
                                mAlias[var] = result.text;
                            }
                            else
                            {
                                var text = result.text;
                                object v;
                                if (result.type == KeywordType.Content)
                                    mValues[var] = text;
                                else if (CompileBuiltinValue(text, out v))
                                    mValues[var] = v;
                                else
                                    mValues[var] = text;
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidFormulaException(reader.ToString());
                    }
                }
            }
        }

        void ExecuteRuntimeCmd(CompilingState rt, bool isAlias, SitcomFile file)
        {
            while (!file.Eof)
            {
                string var = null;
                SitcomFile.Keyword result = default;
                if (file.NextKeyword())
                {
                    var keyword = file.keyword;
                    if (keyword.type == KeywordType.Content || keyword.type == KeywordType.Keyword)
                        var = keyword.text;
                    else
                        throw new InvalidFormulaException(file.ToString());
                }
                else
                {
                    break;
                }
                if (!file.NextKeyword() || file.keyword.id != (int)'=')
                {
                    throw new InvalidFormulaException(file.ToString());
                }
                if (file.NextKeyword())
                {
                    var keyword = file.keyword;
                    if (keyword.type == KeywordType.Keyword || keyword.type == KeywordType.Content)
                        result = keyword;
                    else
                        throw new InvalidFormulaException(file.ToString());
                }
                else
                {
                    throw new InvalidFormulaException(file.ToString());
                }
                if (isAlias)
                {
                    rt.alias[var] = result.text;
                }
                else
                {
                    var text = result.text;
                    object v;
                    if (result.type == KeywordType.Content)
                        rt.values[var] = text;
                    else if (CompileBuiltinValue(text, out v))
                        rt.values[var] = v;
                    else
                        rt.values[var] = text;
                }
            }
        }

        /// <summary>
        /// 编译公式运算对象
        /// </summary>
        /// <param name="formula"></param>
        /// <returns></returns>
        /// <exception cref="System.Exception"></exception>
        public IFormula CompileFormula(string formula)
        {
            if (string.IsNullOrEmpty(formula))
                return null;
            if (!mInitialized)
                throw new System.Exception("Formula factory is not ready for compiling formula.");
            var state = mRtPools.Get();
            state.factory = this;
            var reader = new SitcomFile();
            reader.Load(formula);
            reader.BeginRead();
            FormulaOperator current = null;
            while (reader.StartLine())
            {
                bool first = true;
                while (reader.NextKeyword())
                {
                    var keyword = reader.keyword;
                    if (first && (keyword.id == mAliasCmdId || keyword.id == mValueCmdId))
                    {
                        ExecuteRuntimeCmd(state, keyword.id == mAliasCmdId, reader);
                        break;
                    }
                    first = false;
                    FormulaOperator oper;
                    if (mFormulaOperators.TryGetValue(keyword.id, out oper))
                    {
                        oper = oper.Clone(keyword.text, keyword.type == KeywordType.Content);
                        oper.file = keyword;
                    }
                    else
                    {
                        //var txt = keyword.text;
                        //txt = mAliasTarget.Replace(txt, EvaluateAlias);
                        oper = mAtomFormula.Clone(keyword.text, keyword.type == KeywordType.Content);
                        oper.file = keyword;
                    }
                    if (current == null)
                    {
                        current = oper;
                    }
                    else
                    {
                        while (!current.isBlocked && current.priority > oper.priority)
                        {
                            var p = current.parent;
                            if (p != null)
                                current = p;
                            else
                                break;
                        }
                        if (current.isBlocked || current.priority < oper.priority)
                        {
                            current = current.JoinRightChild(oper, null);
                        }
                        else
                        {
                            var p = current.parent;
                            if (p != null)
                            {
                                current = p.JoinRightChild(oper, current);
                            }
                            else
                            {
                                oper.JoinLeftChild(current);
                                current = oper;
                            }
                        }
                    }

                }
            }
            while (current != null && current.parent != null)
            {
                current = current.parent;
            }
            current.FinishCompile(state);
            state.Clear();
            mRtPools.Add(state);
            return current.CompiliedFormula;
        }

        public void GetPrecompiledValues(IFormula formula, ICollection<object> results)
        {
            if (results == null)
                return;
            if (formula is FormulaOperator oper)
            {
                System.Action<FormulaOperator> action = (fo) =>
                {
                    object result;
                    if (fo.GetPrecompileResult(true, out result))
                        results.Add(result);
                };
                oper.ExecuteRecursive(action);
            }
            else if (formula is FormulaResult res)
            {
                results.Add(res.GetFormulaResult(null));
            }
        }

        // 运算符
        internal abstract class FormulaOperator : IFormulaSource, IFormula
        {
            internal SitcomFile.Keyword file;
            internal string keyword;
            public readonly int id;
            public readonly int priority;
            internal FormulaOperator parent;
            object mPrecompileValue;
            bool mHasPrecompileValue;
            int IFormulaSource.Id { get { return id; } }
            string IFormulaSource.Keyword { get { return keyword; } }
            internal bool IsPrecompilied { get { return mHasPrecompileValue; } }
            internal IFormula CompiliedFormula
            {
                get
                {
                    if (IsPrecompilied)
                        return mPrecompileValue is IFormula ? (IFormula)mPrecompileValue : new FormulaResult(mPrecompileValue);
                    else
                        return this;
                }
            }
            internal FormulaOperator(int id, int priority)
            {
                this.id = id;
                this.priority = priority;
            }
            protected void SetPrecompileResult(object value)
            {
                mHasPrecompileValue = true;
                mPrecompileValue = value;
            }
            internal bool GetPrecompileResult(bool wantFormula, out object result)
            {
                if (mHasPrecompileValue && (wantFormula || !(mPrecompileValue is IFormula)))
                {
                    result = mPrecompileValue;
                    return true;
                }
                else
                {
                    result = null;
                    return false;
                }
            }

            public object GetFormulaResult(object context)
            {
                if (mHasPrecompileValue)
                {
                    if (mPrecompileValue is IFormula)
                        return ((IFormula)mPrecompileValue).GetFormulaResult(context);
                    else
                        return mPrecompileValue;
                }
                else
                {
                    return CalculateFormulaResult(context);
                }
            }

            protected abstract object CalculateFormulaResult(object context);
            internal abstract void SetFormulaOutput(IFormulaOutput output);
            internal abstract bool isBlocked { get; }
            internal abstract FormulaOperator Clone(string keyword, bool isText);
            // 左连接
            internal abstract void JoinLeftChild(FormulaOperator value);
            // 右连接
            internal abstract FormulaOperator JoinRightChild(FormulaOperator value, FormulaOperator replacedChild);
            // finish compile 应该从子节点优先执行
            internal abstract void FinishCompile(CompilingState state);
            internal abstract void ExecuteRecursive(System.Action<FormulaOperator> action);
            public override string ToString()
            {
                if (IsPrecompilied)
                {
                    if (mPrecompileValue == null)
                        return "COMPILIED(null)";
                    else if (mPrecompileValue is IFormula)
                        return mPrecompileValue.ToString();
                    else
                        return string.Format("COMPILIED({0})", mPrecompileValue);
                }
                else
                    return string.Format("COMPILIED({0})", keyword);
            }
        }

        enum EAtomType
        {
            Auto,
            Function,
            Variable,
            Subfix,
        }

        // 原子表达式
        class AtomFormula : FormulaOperator
        {
            bool mIsText;
            readonly EAtomType mType;
            BracketFormula mChild;
            IReferenceFormulaOutput mRefOutput;
            IFunctionFormulaOutput mFuncOutput;
            IReferenceFormulaCompilier mRefCompilier;
            IFunctionFormulaCompilier mFuncCompilier;
            IPrecompileReferenceFormula mPrecompileRef;
            IPrecompileFunctionFormula mPrecompileFunc;
            internal override bool isBlocked { get { return false; } }
            internal AtomFormula(int id, int priority, EAtomType type) : base(id, priority)
            {
                mType = type;
            }
            internal override void SetFormulaOutput(IFormulaOutput output)
            {
                bool valid = false;
                if (output is IReferenceFormulaOutput)
                {
                    valid = true;
                    mRefOutput = (IReferenceFormulaOutput)output;
                }
                if (output is IFunctionFormulaOutput)
                {
                    valid = true;
                    mFuncOutput = (IFunctionFormulaOutput)output;
                }
                if (output is IReferenceFormulaCompilier)
                {
                    valid = true;
                    mRefCompilier = (IReferenceFormulaCompilier)output;
                }
                if (output is IFunctionFormulaCompilier)
                {
                    valid = true;
                    mFuncCompilier = (IFunctionFormulaCompilier)output;
                }
                if (output is IPrecompileReferenceFormula)
                {
                    valid = true;
                    mPrecompileRef = (IPrecompileReferenceFormula)output;
                }
                if (output is IPrecompileFunctionFormula)
                {
                    valid = true;
                    mPrecompileFunc = (IPrecompileFunctionFormula)output;
                }
                if (!valid)
                    throw new InvalidFormulaOutputException(output, "atom");
            }
            internal override void JoinLeftChild(FormulaOperator value)
            {
                throw new InvalidFormulaException(file.content);
            }

            internal override FormulaOperator JoinRightChild(FormulaOperator value, FormulaOperator replaced)
            {
                if (mType == EAtomType.Subfix)
                    throw new InvalidFormulaException(file.content);
                if (mType == EAtomType.Variable || mChild != null || !(value is BracketFormula) || replaced != null)
                    throw new InvalidFormulaException(value.file.content);
                mChild = (BracketFormula)value;
                value.parent = this;
                return value;
            }

            internal override void ExecuteRecursive(System.Action<FormulaOperator> action)
            {
                action(this);
                if (mChild != null)
                    mChild.ExecuteRecursive(action);
            }
            internal override void FinishCompile(CompilingState state)
            {
                if (mType == EAtomType.Subfix || (mType == EAtomType.Function && mChild == null))
                    throw new InvalidFormulaException(file.content);
                bool hasValue = false;
                object value = null;
                if (mChild != null)
                {
                    mChild.FinishCompile(state);

                    var rt = state.factory.mStackPools.Get();
                    rt.mState = state;

                    if (mFuncCompilier != null && !mFuncCompilier.CompileOutput(rt, this, mChild))
                        throw new InvalidFormulaException(file.content);

                    keyword = rt.GetRealKeyword(keyword);
                    object arg;
                    if (mPrecompileFunc != null && mChild.GetPrecompileResult(mPrecompileFunc.IsFormulaInputEnabled, out arg))
                    {
                        hasValue = mPrecompileFunc.OnPrecompile(rt, keyword, arg, out value);
                    }

                    rt.Clear();
                    state.factory.mStackPools.Add(rt);
                }
                else
                {
                    if (mIsText)
                    {
                        hasValue = true;
                        value = keyword;
                    }
                    else
                    {
                        hasValue = CompileBuiltinValue(keyword, out value);
                    }

                    if (!hasValue)
                    {
                        var rt = state.factory.mStackPools.Get();
                        rt.mState = state;
                        if (mRefCompilier != null && !mRefCompilier.CompileOutput(rt, this))
                            throw new InvalidFormulaException(file.content);
                        keyword = rt.GetRealKeyword(keyword);
                        hasValue = mPrecompileRef != null && mPrecompileRef.OnPrecompile(rt, keyword, out value);
                        if (!hasValue)
                        {
                            hasValue = rt.GetPredefinedValue(keyword, out value);
                        }
                        rt.Clear();
                        state.factory.mStackPools.Add(rt);
                    }
                    else if (mIsText && mPrecompileRef != null && mPrecompileRef.IsTextCompilier)
                    {
                        var rt = state.factory.mStackPools.Get();
                        rt.mState = state;
                        object result;
                        var v = mPrecompileRef.OnPrecompile(rt, keyword, out result);
                        if (v)
                        {
                            hasValue = true;
                            value = result;
                        }
                        rt.Clear();
                        state.factory.mStackPools.Add(rt);
                    }
                }
                if (hasValue)
                {
                    SetPrecompileResult(value);
                }
            }

            internal override FormulaOperator Clone(string keyword, bool isText)
            {
                var oper = new AtomFormula(id, priority, isText ? EAtomType.Variable : mType);
                oper.keyword = keyword;
                oper.mIsText = isText;
                oper.mRefOutput = this.mRefOutput;
                oper.mFuncOutput = this.mFuncOutput;
                oper.mRefCompilier = this.mRefCompilier;
                oper.mFuncCompilier = this.mFuncCompilier;
                oper.mPrecompileRef = this.mPrecompileRef;
                oper.mPrecompileFunc = this.mPrecompileFunc;
                return oper;
            }

            protected override object CalculateFormulaResult(object context)
            {
                if (mChild == null)
                    return mRefOutput == null ? keyword : mRefOutput.GetFormulaResult(context, keyword);
                else
                    return mFuncOutput == null ? null : mFuncOutput.GetFormulaResult(context, keyword, mChild.GetFormulaResult(context));
            }
            public override string ToString()
            {
                if (IsPrecompilied)
                    return base.ToString();
                else
                    return string.Format("{0}{1}", keyword, mChild);
            }

        }

        // 右连接单目运算符
        class RightJoinFormula : FormulaOperator
        {
            FormulaOperator child;
            IUnaryFormulaOutput mOutput;
            IUnaryFormulaCompilier mCompilier;
            IPrecompileUnaryFormula mPrecompile;
            internal override bool isBlocked { get { return child == null; } }
            internal RightJoinFormula(int id, int priority) : base(id, priority)
            {
            }
            internal override void SetFormulaOutput(IFormulaOutput output)
            {
                bool valid = false;
                if (output is IUnaryFormulaOutput)
                {
                    valid = true;
                    mOutput = (IUnaryFormulaOutput)output;
                }
                if (output is IUnaryFormulaCompilier)
                {
                    valid = true;
                    mCompilier = (IUnaryFormulaCompilier)output;
                }
                if (output is IPrecompileUnaryFormula)
                {
                    valid = true;
                    mPrecompile = (IPrecompileUnaryFormula)output;
                }
                if (!valid)
                    throw new InvalidFormulaOutputException(output, file.content);
            }
            internal override void JoinLeftChild(FormulaOperator value)
            {
                throw new InvalidFormulaException(file.content);
            }

            internal override FormulaOperator JoinRightChild(FormulaOperator value, FormulaOperator replaced)
            {
                if (replaced != null && replaced != child)
                    throw new InvalidFormulaException(value.file.content);
                var it = child;
                child = value;
                value.parent = this;
                if (it != null)
                    value.JoinLeftChild(it);
                return value;
            }
            internal override void ExecuteRecursive(System.Action<FormulaOperator> action)
            {
                action(this);
                if (child != null)
                    child.ExecuteRecursive(action);
            }
            internal override void FinishCompile(CompilingState state)
            {
                if (child == null)
                    throw new InvalidFormulaException(keyword);
                child.FinishCompile(state);
                var rt = state.factory.mStackPools.Get();
                rt.mState = state;
                if (mCompilier != null && !mCompilier.CompileOutput(rt, this, child))
                    throw new InvalidFormulaException(keyword);
                object arg;
                if (mPrecompile != null && child.GetPrecompileResult(mPrecompile.IsFormulaInputEnabled, out arg))
                {
                    object value;
                    if (mPrecompile.OnPrecompile(rt, arg, out value))
                    {
                        SetPrecompileResult(value);
                    }
                }
                rt.Clear();
                state.factory.mStackPools.Add(rt);
            }
            internal override FormulaOperator Clone(string keyword, bool isText)
            {
                var oper = new RightJoinFormula(this.id, this.priority);
                oper.keyword = this.keyword;
                oper.mOutput = this.mOutput;
                oper.mCompilier = this.mCompilier;
                oper.mPrecompile = this.mPrecompile;
                return oper;
            }

            protected override object CalculateFormulaResult(object context)
            {
                if (mOutput == null)
                    return null;
                var obj = child == null ? null : child.GetFormulaResult(context);
                return mOutput.GetFormulaResult(context, obj);
            }

            public override string ToString()
            {
                if (IsPrecompilied)
                    return base.ToString();
                else
                    return string.Format("{0} {1}", keyword, child);
            }
        }

        // 双目运算符
        class BinFormula : FormulaOperator
        {
            internal FormulaOperator left, right;
            IBinaryFormulaOutput mOutput;
            IBinaryFormulaCompilier mCompilier;
            IPrecompileBinaryFormula mPrecompile;
            internal override bool isBlocked { get { return right == null; } }
            internal BinFormula(int id, int priority) : base(id, priority) { }

            internal override void SetFormulaOutput(IFormulaOutput output)
            {
                bool valid = false;
                if (output is IBinaryFormulaOutput)
                {
                    valid = true;
                    mOutput = (IBinaryFormulaOutput)output;
                }
                if (output is IBinaryFormulaCompilier)
                {
                    valid = true;
                    mCompilier = (IBinaryFormulaCompilier)output;
                }
                if (output is IPrecompileBinaryFormula)
                {
                    valid = true;
                    mPrecompile = (IPrecompileBinaryFormula)output;
                }
                if (!valid)
                    throw new InvalidFormulaOutputException(output, keyword);
            }

            internal override void JoinLeftChild(FormulaOperator value)
            {
                if (left != null)
                    throw new InvalidFormulaException(file.content);
                left = value;
                value.parent = this;
            }

            internal override FormulaOperator JoinRightChild(FormulaOperator value, FormulaOperator replaced)
            {
                if (replaced != null && replaced != right)
                    throw new InvalidFormulaException(value.file.content);
                var it = right;
                right = value;
                value.parent = this;
                if (it != null)
                    value.JoinLeftChild(it);
                return value;
            }
            internal override void ExecuteRecursive(System.Action<FormulaOperator> action)
            {
                action(this);
                if (left != null)
                    left.ExecuteRecursive(action);
                if (right != null)
                    right.ExecuteRecursive(action);
            }
            internal override void FinishCompile(CompilingState state)
            {
                if (left == null || right == null)
                    throw new InvalidFormulaException(file.content);
                left.FinishCompile(state);
                right.FinishCompile(state);
                var rt = state.factory.mStackPools.Get();
                rt.mState = state;
                if (mCompilier != null && !mCompilier.CompileOutput(rt, this, left, right))
                    throw new InvalidFormulaException(file.content);
                object a, b;
                if (mPrecompile != null)
                {
                    var wantFormula = mPrecompile.IsFormulaInputEnabled;
                    if (left.GetPrecompileResult(wantFormula, out a) && right.GetPrecompileResult(wantFormula, out b))
                    {
                        object value;
                        if (mPrecompile.OnPrecompile(rt, a, b, out value))
                        {
                            SetPrecompileResult(value);
                        }
                    }
                }
                rt.Clear();
                state.factory.mStackPools.Add(rt);
            }
            internal override FormulaOperator Clone(string keyword, bool isText)
            {
                var oper = new BinFormula(id, priority);
                oper.keyword = this.keyword;
                oper.mOutput = this.mOutput;
                oper.mCompilier = this.mCompilier;
                oper.mPrecompile = this.mPrecompile;
                return oper;
            }
            protected override object CalculateFormulaResult(object context)
            {
                if (mOutput == null)
                    return null;
                var a = left == null ? null : left.GetFormulaResult(context);
                var b = right == null ? null : right.GetFormulaResult(context);
                return mOutput.GetFormulaResult(context, a, b);
            }
            public override string ToString()
            {
                if (IsPrecompilied)
                    return base.ToString();
                else
                    return string.Format("{0} {1} {2}", left, keyword, right);
            }
        }

        class PrecompiliedArray : IFormula
        {
            object[] args;
            object[] finalValues;
            public PrecompiliedArray(object[] values)
            {
                args = values;
            }

            public object GetFormulaResult(object context)
            {
                if (args != null)
                {
                    if (finalValues == null)
                        finalValues = new object[args.Length];
                    for (int i = 0; i < args.Length; i++)
                    {
                        var v = args[i];
                        if (v is IFormula)
                            finalValues[i] = ((IFormula)v).GetFormulaResult(context);
                        else
                            finalValues[i] = v;
                    }
                }
                return finalValues;
            }

            public override string ToString()
            {
                var buf = ParallelUtils.GetBuilder();
                buf.Append("COMPILIED(");
                if (args != null)
                {
                    for (int i = 0; i < args.Length; i++)
                    {
                        if (i > 0)
                            buf.Append(',');
                        buf.Append(args[i]);
                    }
                }
                buf.Append(')');
                return ParallelUtils.ReturnRelease(buf);
            }
        }

        class BracketFormula : FormulaOperator
        {
            bool mClosed;
            int mCloseId;
            string mCloseKeyword;
            FormulaOperator mChild;
            List<FormulaOperator> mChildren;
            int mCommaId;
            internal BracketFormula(int id, int endid, int priority) : base(id, priority)
            {
                mCloseId = endid;
                mCommaId = (int)',';
            }

            internal string ChildKeyword { get { return mChild == null ? null : mChild.keyword; } }
            internal override void SetFormulaOutput(IFormulaOutput output)
            {
                throw new InvalidFormulaOutputException(output, "bracket");
            }
            internal override bool isBlocked { get { return !mClosed; } }

            internal override void JoinLeftChild(FormulaOperator value)
            {
                throw new InvalidFormulaException(file.content);
            }

            internal override FormulaOperator JoinRightChild(FormulaOperator value, FormulaOperator replaced)
            {
                if (mClosed)
                {
                    throw new InvalidFormulaException(value.keyword);
                }
                else if (value.id == mCloseId)
                {
                    mClosed = true;
                    mCloseKeyword = value.keyword;
                    return this;
                }
                else if (value.id == mCommaId)
                {
                    if (mChildren == null)
                        mChildren = new List<FormulaOperator>();
                    mChildren.Add(mChild);
                    mChild = null;
                    return this;
                }
                else
                {
                    if (replaced != null && replaced != mChild)
                        throw new InvalidFormulaException(value.file.content);
                    var it = mChild;
                    mChild = value;
                    value.parent = this;
                    if (it != null)
                        value.JoinLeftChild(it);
                    return value;
                }
            }
            internal override void ExecuteRecursive(System.Action<FormulaOperator> action)
            {
                action(this);
                if (mChild != null)
                {
                    mChild.ExecuteRecursive(action);
                }
                else if (mChildren != null)
                {
                    for (int i = 0; i < mChildren.Count; i++)
                    {
                        mChildren[i]?.ExecuteRecursive(action);
                    }
                }
            }

            internal override void FinishCompile(CompilingState state)
            {
                if (!mClosed)
                    throw new InvalidFormulaException(file.content);
                if (mChild != null && mChildren != null)
                {
                    mChildren.Add(mChild);
                    mChild = null;
                }
                object value;
                if (mChild != null)
                {
                    mChild.FinishCompile(state);
                    if (mChild.GetPrecompileResult(true, out value))
                        SetPrecompileResult(value);
                }
                else if (mChildren != null)
                {
                    byte preValueType = 1; // 0: none, 1: const, 2: formula
                    object[] values = new object[mChildren.Count];
                    for (int i = 0; i < mChildren.Count; i++)
                    {
                        var child = mChildren[i];
                        bool hasValue;
                        if (child != null)
                        {
                            child.FinishCompile(state);
                            hasValue = child.GetPrecompileResult(true, out value);
                        }
                        else
                        {
                            value = null;
                            hasValue = true;
                        }
                        if (preValueType != 0 && hasValue)
                        {
                            values[i] = value;
                            if (value is IFormula)
                                preValueType = 2;
                        }
                        else
                        {
                            preValueType = 0;
                        }
                    }
                    if (preValueType == 1)
                        SetPrecompileResult(values);
                    else if (preValueType == 2)
                        SetPrecompileResult(new PrecompiliedArray(values));
                }
                else
                {
                    SetPrecompileResult(null);
                }
            }
            internal override FormulaOperator Clone(string keyword, bool isText)
            {
                var oper = new BracketFormula(id, mCloseId, priority);
                oper.keyword = this.keyword;
                return oper;
            }
            protected override object CalculateFormulaResult(object context)
            {
                if (mChildren != null)
                {
                    var values = new object[mChildren.Count];
                    for (int i = 0; i < values.Length; i++)
                    {
                        values[i] = mChildren[i].GetFormulaResult(context);
                    }
                    return values;
                }
                else if (mChild != null)
                {
                    return mChild.GetFormulaResult(context);
                }
                else
                {
                    return null;
                }
            }
            public override string ToString()
            {
                if (IsPrecompilied)
                    return base.ToString();
                else if (mChildren != null)
                    return string.Format("{0}{1}{2}", keyword, ParallelUtils.Gather(mChildren), mCloseKeyword);
                else
                    return string.Format("{0}{1}{2}", keyword, mChild, mCloseKeyword);
            }
        }
    }
}
